use std::{
    any::{Any, TypeId},
    collections::HashMap,
    fmt::{self, Display, Formatter},
    hash::Hash,
};

use crate::function::FunctionMeta;

pub type TypeInfoFn = fn() -> TypeInfo;

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TypeKind {
    Unknown,
    Object(Option<TypeInfoFn>),
    UnitStruct(Vec<TypeInfoFn>),
    NamedStruct(Vec<TypeInfoFn>),
    TupleStruct(Vec<TypeInfoFn>),
    Enum(Vec<TypeInfoFn>),
    Bool,
    I8,
    U8,
    I16,
    U16,
    I32,
    Isize,
    U32,
    Usize,
    I64,
    U64,
    I128,
    U128,
    F32,
    F64,
    String,
    File,
    Option(TypeInfoFn),
    Result(TypeInfoFn, TypeInfoFn),
    Array(TypeInfoFn, usize),
    Tuple(Vec<TypeInfoFn>),
    Vec(TypeInfoFn),
    Set(TypeInfoFn),
    Map(TypeInfoFn, TypeInfoFn),
}

impl Display for TypeKind {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            TypeKind::Object(_) => write!(f, "Object"),
            TypeKind::UnitStruct(_) => write!(f, "UnitStruct"),
            TypeKind::NamedStruct(_) => write!(f, "NamedStruct"),
            TypeKind::TupleStruct(_) => write!(f, "TupleStruct"),
            TypeKind::Enum(_) => write!(f, "Enum"),
            TypeKind::Bool => write!(f, "Bool"),
            TypeKind::I8 => write!(f, "I8"),
            TypeKind::U8 => write!(f, "U8"),
            TypeKind::I16 => write!(f, "I16"),
            TypeKind::U16 => write!(f, "U16"),
            TypeKind::I32 => write!(f, "I32"),
            TypeKind::Isize => write!(f, "Isize"),
            TypeKind::U32 => write!(f, "U32"),
            TypeKind::Usize => write!(f, "Usize"),
            TypeKind::I64 => write!(f, "I64"),
            TypeKind::U64 => write!(f, "U64"),
            TypeKind::I128 => write!(f, "I128"),
            TypeKind::U128 => write!(f, "U128"),
            TypeKind::F32 => write!(f, "F32"),
            TypeKind::F64 => write!(f, "F64"),
            TypeKind::String => write!(f, "String"),
            TypeKind::File => write!(f, "File"),
            TypeKind::Option(_) => write!(f, "Option"),
            TypeKind::Result(_, _) => write!(f, "Result"),
            TypeKind::Array(_, _) => write!(f, "Array"),
            TypeKind::Tuple(_) => write!(f, "Tuple"),
            TypeKind::Vec(_) => write!(f, "Vec"),
            TypeKind::Set(_) => write!(f, "Set"),
            TypeKind::Map(_, _) => write!(f, "Map"),
            TypeKind::Unknown => write!(f, "Unknown"),
        }
    }
}

#[derive(Clone, Default, Debug, PartialEq, Eq)]
pub struct FieldSerde {
    pub rename_serialize: Option<String>,
    pub rename_deserialize: Option<String>,
    pub default: bool,
    pub skip_serialize: bool,
    pub skip_deserialize: bool,
    pub skip: bool,
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum ConstraitType {
    Length(usize, usize, Option<String>),
    Range(String, String, Option<String>),
    Constant(String, Option<String>),
    Regex(String, Option<String>),
    Enumer(TypeInfoFn, Option<String>),
    SelfValidate(Option<String>),
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Field {
    pub docs: Option<String>,
    pub index: usize,
    pub ident: Option<String>,
    pub name: Option<String>,
    pub type_info_fn: TypeInfoFn,
    pub attrs: Vec<String>,
    pub serde: FieldSerde,
    pub constraits: Vec<ConstraitType>,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Variant {
    pub docs: Option<String>,
    pub index: usize,
    pub value: Option<isize>,
    pub ident: String,
    pub fields: Fields,
    pub attrs: Vec<String>,
    pub serde: VariantSerde,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Fields {
    pub fields: Vec<Field>,
    pub named: bool,
}

#[derive(Clone, Default, Debug, PartialEq, Eq)]
pub struct VariantSerde {
    pub rename_serialize: Option<String>,
    pub rename_deserialize: Option<String>,
    pub rename_all: Option<String>,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Members {
    Fields(Fields),
    Variants(Vec<Variant>),
}

#[derive(Clone, Default, Debug, PartialEq, Eq)]
pub struct ContainerSerde {
    pub rename_serialize: Option<String>,
    pub rename_deserialize: Option<String>,
    pub rename_all: Option<String>,
}

pub struct TypeInfo {
    pub type_id: TypeId,
    pub type_name: String,
    pub docs: Option<String>,
    pub kind: TypeKind,
    pub ident: String,
    pub attrs: Vec<String>,
    pub members: Members,
    pub funcs: Vec<FunctionMeta>,
    pub serde: ContainerSerde,
}

#[derive(Debug)]
pub struct Object {
    pub type_info_fn: TypeInfoFn,
    pub value: Value,
}

#[derive(Debug)]
pub enum Value {
    Nil,
    UnitStruct,
    NamedStruct(HashMap<String, Value>),
    TupleStruct(Vec<Value>),
    Enum(String, Box<Value>),
    Bool(bool),
    I8(i8),
    U8(u8),
    I16(i16),
    U16(u16),
    I32(i32),
    Isize(isize),
    U32(u32),
    Usize(usize),
    I64(i64),
    U64(u64),
    I128(i128),
    U128(u128),
    F32(f32),
    F64(f64),
    String(String),
    File(Vec<u8>),
    Option(Option<Box<Value>>),
    Result(Result<Box<Value>, Box<Value>>),
    Array(Vec<Value>),
    Tuple(Vec<Value>),
    Vec(Vec<Value>),
    Set(Vec<Value>),
    Map(HashMap<Value, Value>),
    Any(Box<dyn Any + Send + Sync>),
}

impl Display for Value {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            Value::Nil => write!(f, "Nil"),
            Value::UnitStruct => write!(f, "UnitStruct"),
            Value::NamedStruct(_) => write!(f, "NamedStruct"),
            Value::TupleStruct(_) => write!(f, "TupleStruct"),
            Value::Enum(_, _) => write!(f, "Enum"),
            Value::Bool(_) => write!(f, "Bool"),
            Value::I8(_) => write!(f, "I8"),
            Value::U8(_) => write!(f, "U8"),
            Value::I16(_) => write!(f, "I16"),
            Value::U16(_) => write!(f, "U16"),
            Value::I32(_) => write!(f, "I32"),
            Value::U32(_) => write!(f, "U32"),
            Value::Usize(_) => write!(f, "Usize"),
            Value::Isize(_) => write!(f, "Isize"),
            Value::I64(_) => write!(f, "I64"),
            Value::U64(_) => write!(f, "U64"),
            Value::I128(_) => write!(f, "I128"),
            Value::U128(_) => write!(f, "U128"),
            Value::F32(_) => write!(f, "F32"),
            Value::F64(_) => write!(f, "F64"),
            Value::String(_) => write!(f, "String"),
            Value::File(_) => write!(f, "File"),
            Value::Option(_) => write!(f, "Option"),
            Value::Result(_) => write!(f, "Result"),
            Value::Array(_) => write!(f, "Array"),
            Value::Tuple(_) => write!(f, "Tuple"),
            Value::Vec(_) => write!(f, "Vec"),
            Value::Set(_) => write!(f, "Set"),
            Value::Map(_) => write!(f, "Map"),
            Value::Any(_) => write!(f, "Any"),
        }
    }
}

impl Default for Value {
    fn default() -> Self {
        Self::Nil
    }
}

impl PartialEq for Value {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Self::NamedStruct(l0), Self::NamedStruct(r0)) => l0 == r0,
            (Self::TupleStruct(l0), Self::TupleStruct(r0)) => l0 == r0,
            (Self::Enum(l0, l1), Self::Enum(r0, r1)) => l0 == r0 && l1 == r1,
            (Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
            (Self::I8(l0), Self::I8(r0)) => l0 == r0,
            (Self::U8(l0), Self::U8(r0)) => l0 == r0,
            (Self::I16(l0), Self::I16(r0)) => l0 == r0,
            (Self::U16(l0), Self::U16(r0)) => l0 == r0,
            (Self::I32(l0), Self::I32(r0)) => l0 == r0,
            (Self::U32(l0), Self::U32(r0)) => l0 == r0,
            (Self::Usize(l0), Self::Usize(r0)) => l0 == r0,
            (Self::I64(l0), Self::I64(r0)) => l0 == r0,
            (Self::U64(l0), Self::U64(r0)) => l0 == r0,
            (Self::I128(l0), Self::I128(r0)) => l0 == r0,
            (Self::U128(l0), Self::U128(r0)) => l0 == r0,
            (Self::F32(l0), Self::F32(r0)) => l0 == r0,
            (Self::F64(l0), Self::F64(r0)) => l0 == r0,
            (Self::String(l0), Self::String(r0)) => l0 == r0,
            (Self::Option(l0), Self::Option(r0)) => l0 == r0,
            (Self::Array(l0), Self::Array(r0)) => l0 == r0,
            (Self::Tuple(l0), Self::Tuple(r0)) => l0 == r0,
            (Self::Vec(l0), Self::Vec(r0)) => l0 == r0,
            (Self::Set(l0), Self::Set(r0)) => l0 == r0,
            (Self::Map(l0), Self::Map(r0)) => l0.iter().all(|(k, v)| (r0.get(k) == Some(v))),
            (Self::Any(l0), Self::Any(r0)) => (**l0).type_id() == (**r0).type_id(),
            _ => core::mem::discriminant(self) == core::mem::discriminant(other),
        }
    }
}

impl Eq for Value {}
impl Hash for Value {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        core::mem::discriminant(self).hash(state);
    }
}
